package service

import (
	"errors"
	"fmt"
	"framework-server/global"
	"framework-server/model"
	"framework-server/model/request"
	"framework-server/utils"

	"gorm.io/gorm"
)

//@author: [piexlmax](https://github.com/piexlmax)
//@function: CreateApi
//@description: 新增基础api
//@param: api model.SysApi
//@return: err error

func CreateApi(api model.SysApi) (err error) {
	if api.ButtonId != 0 && api.MenuId != 0{
		err = errors.New("菜单和按钮只能同时绑定一个")
		return
	}
	if !errors.Is(global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&model.SysApi{}).Error, gorm.ErrRecordNotFound) {
		return errors.New("存在相同api")
	}
	err = global.GVA_DB.Create(&api).Error
	if err != nil{
		return
	}
	if api.Public { //如果是公共接口，则所有角色都关联这个接口
		err2, authorityList := GetAllAuthorityList()
		if err2 != nil {
			err = err2
			return
		}
		for _, authority := range authorityList {
			err = AddCasbinApi(authority.AuthorityId, api.Path, api.Method)
			if err != nil {
				return
			}
		}
	}else{
		err3 ,menuAuthorityIds := GetAuthorityByMenu(api.MenuId)
		if err3 != nil{
			err = err3
			return
		}
		err4 ,buttonAuthorityIds := GetAuthorityByButton(api.MenuId)
		if err4 != nil{
			err = err4
			return
		}
		for _, v := range menuAuthorityIds{
			AddCasbinApi(v, api.Path, api.Method)
		}
		for _, v := range buttonAuthorityIds{
			AddCasbinApi(v, api.Path, api.Method)
		}
	}
	return
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteApi
//@description: 删除基础api
//@param: api model.SysApi
//@return: err error

func DeleteApi(api model.SysApi) (err error) {
	err = global.GVA_DB.Delete(&api).Error
	ClearCasbin(1, api.Path, api.Method)
	return err
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetAPIInfoList
//@description: 分页获取数据,
//@param: api model.SysApi, info request.PageInfo, order string, desc bool
//@return: err error

func GetAPIInfoList(api model.SysApi, info request.PageInfo, order string, desc bool) (err error, list interface{}, total int64) {
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	db := global.GVA_DB.Model(&model.SysApi{})
	var apiList []model.SysApi

	if api.Path != "" {
		db = db.Where("path LIKE ?", "%"+api.Path+"%")
	}

	if api.Description != "" {
		db = db.Where("description LIKE ?", "%"+api.Description+"%")
	}

	if api.Method != "" {
		db = db.Where("method = ?", api.Method)
	}

	if api.ApiGroup != "" {
		db = db.Where("api_group = ?", api.ApiGroup)
	}

	err = db.Count(&total).Error

	if err != nil {
		return err, apiList, total
	} else {
		db = db.Limit(limit).Offset(offset)
		if order != "" {
			var OrderStr string
			if desc {
				OrderStr = order + " desc"
			} else {
				OrderStr = order
			}
			err = db.Order(OrderStr).Find(&apiList).Error
		} else {
			err = db.Order("api_group").Find(&apiList).Error
		}
	}
	return err, apiList, total
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetAllApis
//@description: 获取所有的api
//@return: err error, apis []model.SysApi

func GetAllApis(info request.GetAllApisRequest) (err error, apis []model.SysApi) {
	err = global.GVA_DB.Find(&apis).Error
	if err != nil{
		return err, apis
	}
	if info.ParentId != "0"{
		pathMaps := GetPolicyPathByAuthorityId(info.ParentId)
		newApis := []model.SysApi{}
		newPathMaps := make(map[string]interface{})
		for _, v := range pathMaps{
			key := fmt.Sprintf("%s_%s", v.Path, v.Method)
			newPathMaps[key] = struct {}{}
		}
		for _, v := range apis{
			key := fmt.Sprintf("%s_%s", v.Path, v.Method)
			if _, ok := newPathMaps[key]; ok{
				newApis = append(newApis, v)
			}
		}
		return nil, newApis
	}
	return nil, apis
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetApiById
//@description: 根据id获取api
//@param: id float64
//@return: err error, api model.SysApi

func GetApiById(id float64) (err error, api model.SysApi) {
	err = global.GVA_DB.Where("id = ?", id).Preload("ButtonRefer").Preload("MenuRefer").First(&api).Error
	return
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: UpdateApi
//@description: 根据id更新api
//@param: api model.SysApi
//@return: err error

func UpdateApi(api model.SysApi) (err error) {
	if api.ButtonId != 0 && api.MenuId != 0{
		err = errors.New("菜单和按钮只能同时绑定一个")
		return
	}
	var oldA model.SysApi
	err = global.GVA_DB.Where("id = ?", api.ID).First(&oldA).Error
	if oldA.Path != api.Path || oldA.Method != api.Method {
		if !errors.Is(global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&model.SysApi{}).Error, gorm.ErrRecordNotFound) {
			return errors.New("存在相同api路径")
		}

		err = UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method)
		if err != nil {
			return err
		}
	}
	api.MenuRefer = model.SysBaseMenu{}
	if api.Public{  //如果是公共接口，则所有角色都关联这个接口
		api.ButtonId = 0
		api.MenuId = 0
		err2, authorityList := GetAllAuthorityList()
		if err2 != nil{
			err = err2
			return
		}
		for _, authority := range authorityList{
			err = AddCasbinApi(authority.AuthorityId, api.Path, api.Method)
			if err != nil{
				return
			}
		}
	}else{	//如果不是公共接口，则判断所属按钮和所属菜单是否有变化
		if oldA.MenuId != api.MenuId{
			//判断菜单变化情况
			//获取新老的菜单对应的角色，得到需要增加api权限的角色的需要去除api权限的角色
			err3 ,oldAuthorityIds := GetAuthorityByMenu(oldA.MenuId)
			if err3 != nil{
				err = err3
				return
			}
			err4 ,newAuthorityIds := GetAuthorityByMenu(api.MenuId)
			if err4 != nil{
				err = err4
				return
			}
			//获取需要新增的角色
			needAddAuthorityIds := utils.DiffStringArray(newAuthorityIds,oldAuthorityIds)
			for _, v := range needAddAuthorityIds{
				AddCasbinApi(v, api.Path, api.Method)
			}

			//获取需要清除的角色
			needRemoveAuthorityIds := utils.DiffStringArray(oldAuthorityIds, newAuthorityIds)
			for _, v := range needRemoveAuthorityIds{
				DeleteCasbinApi(v, api.Path, api.Method)
			}
		}

		if oldA.ButtonId != api.ButtonId {
			//判断按钮变化情况
			//获取新老按钮对应的角色

			err5 ,oldAuthorityIds := GetAuthorityByButton(oldA.MenuId)
			if err5 != nil{
				err = err5
				return
			}
			err6 ,newAuthorityIds := GetAuthorityByButton(api.MenuId)
			if err6 != nil{
				err = err6
				return
			}
			//获取需要新增的角色
			needAddAuthorityIds := utils.DiffStringArray(newAuthorityIds,oldAuthorityIds)
			for _, v := range needAddAuthorityIds{
				AddCasbinApi(v, api.Path, api.Method)
			}

			//获取需要清除的角色
			needRemoveAuthorityIds := utils.DiffStringArray(oldAuthorityIds, newAuthorityIds)
			for _, v := range needRemoveAuthorityIds{
				DeleteCasbinApi(v, api.Path, api.Method)
			}
		}
	}
	err = global.GVA_DB.Save(&api).Error

	return
}


//获取角色对应菜单的api接口有哪些
func GetMenuApiByAuthorityId(authorityId string) (err error, apis []model.SysApi){
	var authority model.SysAuthority
	err = global.GVA_DB.Where("authority_id = ?", authorityId).Preload("SysBaseMenus").First(&authority).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, apis
	}
	if err != nil{
		return
	}
	var menuIds = []uint{}
	for _, v := range authority.SysBaseMenus{
		menuIds = append(menuIds, v.ID)
	}
	err = global.GVA_DB.Where("menuId in ?", menuIds).Find(&apis).Error
	return
}


//获取角色对应按钮的api接口有哪些
func GetButtonApiByAuthorityId(authorityId string) (err error, apis []model.SysApi){
	var authority model.SysAuthority
	err = global.GVA_DB.Where("authority_id = ?", authorityId).Preload("SysButtons").First(&authority).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, apis
	}
	if err != nil{
		return
	}
	var buttonIds = []uint{}
	for _, v := range authority.SysButtons{
		buttonIds = append(buttonIds, v.ID)
	}
	err = global.GVA_DB.Where("buttonId in ?", buttonIds).Find(&apis).Error
	return
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteApis
//@description: 删除选中API
//@param: apis []model.SysApi
//@return: err error

func DeleteApisByIds(ids request.IdsReq) (err error) {
	err = global.GVA_DB.Delete(&[]model.SysApi{}, "id in ?", ids.Ids).Error
	return err
}
